﻿using System;
using System.Collections.Generic;
using System.Data;

namespace Job.Core.Data
{
    /// <summary>
    /// 重新封装 IDataReader 的数据读取器(无法继承此类)。
    /// 提供了 GetValue 的范型方法获取 IDataReader 中的值，如果指定列不存在时，则返回类型的默认值，不会抛出异常。
    /// </summary>
    public sealed class DataReader : IDisposable
    {
        #region Properties

        private IDataReader reader;
        private Dictionary<string, int> mapping = new Dictionary<string, int>();
        private bool disposed;

        #endregion

        #region Constructor

        /// <summary>
        /// 初始化 DataReader 类的新实例。
        /// </summary>
        /// <param name="reader">IDataReader。</param>
        public DataReader(IDataReader reader)
        {
            if (reader == null)
                throw new ArgumentNullException("reader");

            this.reader = reader;
        }

        #endregion

        #region Methods

        /// <summary>
        /// 读取下一条记录。
        /// </summary>
        /// <returns>成功读取到下一条记录返回 true，否则返回 false。</returns>
        public bool Read()
        {
            return reader.Read();
        }

        /// <summary>
        /// 从 DataRecord 中获取一个强类型的值。
        /// </summary>
        /// <typeparam name="T">类型。</typeparam>
        /// <param name="name">字段名。</param>
        /// <returns>DataRecord 的值，如果未存在该列，则返回类型的默认值。</returns>
        public T GetValue<T>(string name)
        {
            if (!mapping.ContainsKey(name))
                mapping.Add(name, GetIndex(name));
            
            int index = mapping[name];
            if (index != -1)
            {
                object val = reader[index];
                if (val != DBNull.Value)
                    return (T)val;
            }

            return default(T);
        }

        /// <summary>
        /// 获取指定字段名称所在的索引值，如果未找到返回 -1。
        /// </summary>
        /// <param name="name">字段名称。</param>
        /// <returns>索引值，如果未找到返回 -1。</returns>
        private int GetIndex(string name)
        {
            int count = reader.FieldCount;

            for (int i = 0; i < count; i++)
            {
                if (string.Compare(reader.GetName(i), name, true) == 0)
                    return i;
            }

            return -1;
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            if (!disposed)
            {                
                reader.Close();
                reader.Dispose();
                mapping.Clear();
                GC.SuppressFinalize(this);
                disposed = true;
            }
        }

        #endregion
    }
}
